home *** CD-ROM | disk | FTP | other *** search
/ PC/CD Gamer UK 120 / CD Gamer Issue 120 (March 2003) (Disc 2).ISO / mods / Q2_Codered / codeRED1_0.exe / Data1.cab / sv_user.c < prev    next >
Encoding:
C/C++ Source or Header  |  2002-08-13  |  15.3 KB  |  665 lines

  1. /*
  2. Copyright (C) 1997-2001 Id Software, Inc.
  3.  
  4. This program is free software; you can redistribute it and/or
  5. modify it under the terms of the GNU General Public License
  6. as published by the Free Software Foundation; either version 2
  7. of the License, or (at your option) any later version.
  8.  
  9. This program is distributed in the hope that it will be useful,
  10. but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
  12.  
  13. See the GNU General Public License for more details.
  14.  
  15. You should have received a copy of the GNU General Public License
  16. along with this program; if not, write to the Free Software
  17. Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
  18.  
  19. */
  20. // sv_user.c -- server code for moving users
  21.  
  22. #include "server.h"
  23.  
  24. edict_t    *sv_player;
  25.  
  26. /*
  27. ============================================================
  28.  
  29. USER STRINGCMD EXECUTION
  30.  
  31. sv_client and sv_player will be valid.
  32. ============================================================
  33. */
  34.  
  35. /*
  36. ==================
  37. SV_BeginDemoServer
  38. ==================
  39. */
  40. void SV_BeginDemoserver (void)
  41. {
  42.     char        name[MAX_OSPATH];
  43.  
  44.     Com_sprintf (name, sizeof(name), "demos/%s", sv.name);
  45.     FS_FOpenFile (name, &sv.demofile);
  46.     if (!sv.demofile)
  47.         Com_Error (ERR_DROP, "Couldn't open %s\n", name);
  48. }
  49.  
  50. /*
  51. ================
  52. SV_New_f
  53.  
  54. Sends the first message from the server to a connected client.
  55. This will be sent on the initial connection and upon each server load.
  56. ================
  57. */
  58. void SV_New_f (void)
  59. {
  60.     char        *gamedir;
  61.     int            playernum;
  62.     edict_t        *ent;
  63.  
  64.     Com_DPrintf ("New() from %s\n", sv_client->name);
  65.  
  66.     if (sv_client->state != cs_connected)
  67.     {
  68.         Com_Printf ("New not valid -- already spawned\n");
  69.         return;
  70.     }
  71.  
  72.     // demo servers just dump the file message
  73.     if (sv.state == ss_demo)
  74.     {
  75.         SV_BeginDemoserver ();
  76.         return;
  77.     }
  78.  
  79.     //
  80.     // serverdata needs to go over for all types of servers
  81.     // to make sure the protocol is right, and to set the gamedir
  82.     //
  83.     gamedir = Cvar_VariableString ("gamedir");
  84.  
  85.     // send the serverdata
  86.     MSG_WriteByte (&sv_client->netchan.message, svc_serverdata);
  87.     MSG_WriteLong (&sv_client->netchan.message, PROTOCOL_VERSION);
  88.     MSG_WriteLong (&sv_client->netchan.message, svs.spawncount);
  89.     MSG_WriteByte (&sv_client->netchan.message, sv.attractloop);
  90.     MSG_WriteString (&sv_client->netchan.message, gamedir);
  91.  
  92.     if (sv.state == ss_cinematic || sv.state == ss_pic)
  93.         playernum = -1;
  94.     else
  95.         playernum = sv_client - svs.clients;
  96.     MSG_WriteShort (&sv_client->netchan.message, playernum);
  97.  
  98.     // send full levelname
  99.     MSG_WriteString (&sv_client->netchan.message, sv.configstrings[CS_NAME]);
  100.  
  101.     //
  102.     // game server
  103.     // 
  104.     if (sv.state == ss_game)
  105.     {
  106.         // set up the entity for the client
  107.         ent = EDICT_NUM(playernum+1);
  108.         ent->s.number = playernum+1;
  109.         sv_client->edict = ent;
  110.         memset (&sv_client->lastcmd, 0, sizeof(sv_client->lastcmd));
  111.  
  112.         // begin fetching configstrings
  113.         MSG_WriteByte (&sv_client->netchan.message, svc_stufftext);
  114.         MSG_WriteString (&sv_client->netchan.message, va("cmd configstrings %i 0\n",svs.spawncount) );
  115.     }
  116.  
  117. }
  118.  
  119. /*
  120. ==================
  121. SV_Configstrings_f
  122. ==================
  123. */
  124. void SV_Configstrings_f (void)
  125. {
  126.     int            start;
  127.  
  128.     Com_DPrintf ("Configstrings() from %s\n", sv_client->name);
  129.  
  130.     if (sv_client->state != cs_connected)
  131.     {
  132.         Com_Printf ("configstrings not valid -- already spawned\n");
  133.         return;
  134.     }
  135.  
  136.     // handle the case of a level changing while a client was connecting
  137.     if ( atoi(Cmd_Argv(1)) != svs.spawncount )
  138.     {
  139.         Com_Printf ("SV_Configstrings_f from different level\n");
  140.         SV_New_f ();
  141.         return;
  142.     }
  143.     
  144.     start = atoi(Cmd_Argv(2));
  145.  
  146.     // write a packet full of data
  147.  
  148.     while ( sv_client->netchan.message.cursize < MAX_MSGLEN/2 
  149.         && start < MAX_CONFIGSTRINGS)
  150.     {
  151.         if (sv.configstrings[start][0])
  152.         {
  153.             MSG_WriteByte (&sv_client->netchan.message, svc_configstring);
  154.             MSG_WriteShort (&sv_client->netchan.message, start);
  155.             MSG_WriteString (&sv_client->netchan.message, sv.configstrings[start]);
  156.         }
  157.         start++;
  158.     }
  159.  
  160.     // send next command
  161.  
  162.     if (start == MAX_CONFIGSTRINGS)
  163.     {
  164.         MSG_WriteByte (&sv_client->netchan.message, svc_stufftext);
  165.         MSG_WriteString (&sv_client->netchan.message, va("cmd baselines %i 0\n",svs.spawncount) );
  166.     }
  167.     else
  168.     {
  169.         MSG_WriteByte (&sv_client->netchan.message, svc_stufftext);
  170.         MSG_WriteString (&sv_client->netchan.message, va("cmd configstrings %i %i\n",svs.spawncount, start) );
  171.     }
  172. }
  173.  
  174. /*
  175. ==================
  176. SV_Baselines_f
  177. ==================
  178. */
  179. void SV_Baselines_f (void)
  180. {
  181.     int        start;
  182.     entity_state_t    nullstate;
  183.     entity_state_t    *base;
  184.  
  185.     Com_DPrintf ("Baselines() from %s\n", sv_client->name);
  186.  
  187.     if (sv_client->state != cs_connected)
  188.     {
  189.         Com_Printf ("baselines not valid -- already spawned\n");
  190.         return;
  191.     }
  192.     
  193.     // handle the case of a level changing while a client was connecting
  194.     if ( atoi(Cmd_Argv(1)) != svs.spawncount )
  195.     {
  196.         Com_Printf ("SV_Baselines_f from different level\n");
  197.         SV_New_f ();
  198.         return;
  199.     }
  200.     
  201.     start = atoi(Cmd_Argv(2));
  202.  
  203.     memset (&nullstate, 0, sizeof(nullstate));
  204.  
  205.     // write a packet full of data
  206.  
  207.     while ( sv_client->netchan.message.cursize <  MAX_MSGLEN/2
  208.         && start < MAX_EDICTS)
  209.     {
  210.         base = &sv.baselines[start];
  211.         if (base->modelindex || base->sound || base->effects)
  212.         {
  213.             MSG_WriteByte (&sv_client->netchan.message, svc_spawnbaseline);
  214.             MSG_WriteDeltaEntity (&nullstate, base, &sv_client->netchan.message, true, true);
  215.         }
  216.         start++;
  217.     }
  218.  
  219.     // send next command
  220.  
  221.     if (start == MAX_EDICTS)
  222.     {
  223.         MSG_WriteByte (&sv_client->netchan.message, svc_stufftext);
  224.         MSG_WriteString (&sv_client->netchan.message, va("precache %i\n", svs.spawncount) );
  225.     }
  226.     else
  227.     {
  228.         MSG_WriteByte (&sv_client->netchan.message, svc_stufftext);
  229.         MSG_WriteString (&sv_client->netchan.message, va("cmd baselines %i %i\n",svs.spawncount, start) );
  230.     }
  231. }
  232.  
  233. /*
  234. ==================
  235. SV_Begin_f
  236. ==================
  237. */
  238. void SV_Begin_f (void)
  239. {
  240.     Com_DPrintf ("Begin() from %s\n", sv_client->name);
  241.  
  242.     // handle the case of a level changing while a client was connecting
  243.     if ( atoi(Cmd_Argv(1)) != svs.spawncount )
  244.     {
  245.         Com_Printf ("SV_Begin_f from different level\n");
  246.         SV_New_f ();
  247.         return;
  248.     }
  249.  
  250.     sv_client->state = cs_spawned;
  251.     
  252.     // call the game begin function
  253.     ge->ClientBegin (sv_player);
  254.  
  255.     Cbuf_InsertFromDefer ();
  256. }
  257.  
  258. //=============================================================================
  259.  
  260. /*
  261. ==================
  262. SV_NextDownload_f
  263. ==================
  264. */
  265. void SV_NextDownload_f (void)
  266. {
  267.     int        r;
  268.     int        percent;
  269.     int        size;
  270.  
  271.     if (!sv_client->download)
  272.         return;
  273.  
  274.     r = sv_client->downloadsize - sv_client->downloadcount;
  275.     if (r > 1024)
  276.         r = 1024;
  277.  
  278.     MSG_WriteByte (&sv_client->netchan.message, svc_download);
  279.     MSG_WriteShort (&sv_client->netchan.message, r);
  280.  
  281.     sv_client->downloadcount += r;
  282.     size = sv_client->downloadsize;
  283.     if (!size)
  284.         size = 1;
  285.     percent = sv_client->downloadcount*100/size;
  286.     MSG_WriteByte (&sv_client->netchan.message, percent);
  287.     SZ_Write (&sv_client->netchan.message,
  288.         sv_client->download + sv_client->downloadcount - r, r);
  289.  
  290.     if (sv_client->downloadcount != sv_client->downloadsize)
  291.         return;
  292.  
  293.     FS_FreeFile (sv_client->download);
  294.     sv_client->download = NULL;
  295. }
  296.  
  297. /*
  298. ==================
  299. SV_BeginDownload_f
  300. ==================
  301. */
  302. void SV_BeginDownload_f(void)
  303. {
  304.     char    *name;
  305.     extern    cvar_t *allow_download;
  306.     extern    cvar_t *allow_download_players;
  307.     extern    cvar_t *allow_download_models;
  308.     extern    cvar_t *allow_download_sounds;
  309.     extern    cvar_t *allow_download_maps;
  310.     extern    int        file_from_pak; // ZOID did file come from pak?
  311.     int offset = 0;
  312.  
  313.     name = Cmd_Argv(1);
  314.  
  315.     if (Cmd_Argc() > 2)
  316.         offset = atoi(Cmd_Argv(2)); // downloaded offset
  317.  
  318.     // hacked by zoid to allow more conrol over download
  319.     // first off, no .. or global allow check
  320.     if (strstr (name, "..") || !allow_download->value
  321.         // leading dot is no good
  322.         || *name == '.' 
  323.         // leading slash bad as well, must be in subdir
  324.         || *name == '/'
  325.         // next up, skin check
  326.         || (strncmp(name, "players/", 6) == 0 && !allow_download_players->value)
  327.         // now models
  328.         || (strncmp(name, "models/", 6) == 0 && !allow_download_models->value)
  329.         // now sounds
  330.         || (strncmp(name, "sound/", 6) == 0 && !allow_download_sounds->value)
  331.         // now maps (note special case for maps, must not be in pak)
  332.         || (strncmp(name, "maps/", 6) == 0 && !allow_download_maps->value)
  333.         // MUST be in a subdirectory    
  334.         || !strstr (name, "/") )    
  335.     {    // don't allow anything with .. path
  336.         MSG_WriteByte (&sv_client->netchan.message, svc_download);
  337.         MSG_WriteShort (&sv_client->netchan.message, -1);
  338.         MSG_WriteByte (&sv_client->netchan.message, 0);
  339.         return;
  340.     }
  341.  
  342.  
  343.     if (sv_client->download)
  344.         FS_FreeFile (sv_client->download);
  345.  
  346.     sv_client->downloadsize = FS_LoadFile (name, (void **)&sv_client->download);
  347.     sv_client->downloadcount = offset;
  348.  
  349.     if (offset > sv_client->downloadsize)
  350.         sv_client->downloadcount = sv_client->downloadsize;
  351.  
  352.     if (!sv_client->download
  353.         // special check for maps, if it came from a pak file, don't allow
  354.         // download  ZOID
  355.         || (strncmp(name, "maps/", 5) == 0 && file_from_pak))
  356.     {
  357.         Com_DPrintf ("Couldn't download %s to %s\n", name, sv_client->name);
  358.         if (sv_client->download) {
  359.             FS_FreeFile (sv_client->download);
  360.             sv_client->download = NULL;
  361.         }
  362.  
  363.         MSG_WriteByte (&sv_client->netchan.message, svc_download);
  364.         MSG_WriteShort (&sv_client->netchan.message, -1);
  365.         MSG_WriteByte (&sv_client->netchan.message, 0);
  366.         return;
  367.     }
  368.  
  369.     SV_NextDownload_f ();
  370.     Com_DPrintf ("Downloading %s to %s\n", name, sv_client->name);
  371. }
  372.  
  373.  
  374.  
  375. //============================================================================
  376.  
  377.  
  378. /*
  379. =================
  380. SV_Disconnect_f
  381.  
  382. The client is going to disconnect, so remove the connection immediately
  383. =================
  384. */
  385. void SV_Disconnect_f (void)
  386. {
  387. //    SV_EndRedirect ();
  388.     SV_DropClient (sv_client);    
  389. }
  390.  
  391.  
  392. /*
  393. ==================
  394. SV_ShowServerinfo_f
  395.  
  396. Dumps the serverinfo info string
  397. ==================
  398. */
  399. void SV_ShowServerinfo_f (void)
  400. {
  401.     Info_Print (Cvar_Serverinfo());
  402. }
  403.  
  404.  
  405. void SV_Nextserver (void)
  406. {
  407.     char    *v;
  408.  
  409.     //ZOID, ss_pic can be nextserver'd in coop mode
  410.     if (sv.state == ss_game || (sv.state == ss_pic && !Cvar_VariableValue("coop")))
  411.         return;        // can't nextserver while playing a normal game
  412.  
  413.     svs.spawncount++;    // make sure another doesn't sneak in
  414.     v = Cvar_VariableString ("nextserver");
  415.     if (!v[0])
  416.         Cbuf_AddText ("killserver\n");
  417.     else
  418.     {
  419.         Cbuf_AddText (v);
  420.         Cbuf_AddText ("\n");
  421.     }
  422.     Cvar_Set ("nextserver","");
  423. }
  424.  
  425. /*
  426. ==================
  427. SV_Nextserver_f
  428.  
  429. A cinematic has completed or been aborted by a client, so move
  430. to the next server,
  431. ==================
  432. */
  433. void SV_Nextserver_f (void)
  434. {
  435.     if ( atoi(Cmd_Argv(1)) != svs.spawncount ) {
  436.         Com_DPrintf ("Nextserver() from wrong level, from %s\n", sv_client->name);
  437.         return;        // leftover from last server
  438.     }
  439.  
  440.     Com_DPrintf ("Nextserver() from %s\n", sv_client->name);
  441.  
  442.     SV_Nextserver ();
  443. }
  444.  
  445. typedef struct
  446. {
  447.     char    *name;
  448.     void    (*func) (void);
  449. } ucmd_t;
  450.  
  451. ucmd_t ucmds[] =
  452. {
  453.     // auto issued
  454.     {"new", SV_New_f},
  455.     {"configstrings", SV_Configstrings_f},
  456.     {"baselines", SV_Baselines_f},
  457.     {"begin", SV_Begin_f},
  458.  
  459.     {"nextserver", SV_Nextserver_f},
  460.  
  461.     {"disconnect", SV_Disconnect_f},
  462.  
  463.     // issued by hand at client consoles    
  464.     {"info", SV_ShowServerinfo_f},
  465.  
  466.     {"download", SV_BeginDownload_f},
  467.     {"nextdl", SV_NextDownload_f},
  468.  
  469.     {NULL, NULL}
  470. };
  471.  
  472. /*
  473. ==================
  474. SV_ExecuteUserCommand
  475. ==================
  476. */
  477. void SV_ExecuteUserCommand (char *s)
  478. {
  479.     ucmd_t    *u;
  480.     
  481.     Cmd_TokenizeString (s, false);
  482.     sv_player = sv_client->edict;
  483.  
  484. //    SV_BeginRedirect (RD_CLIENT);
  485.  
  486.     for (u=ucmds ; u->name ; u++)
  487.         if (!strcmp (Cmd_Argv(0), u->name) )
  488.         {
  489.             u->func ();
  490.             break;
  491.         }
  492.  
  493.     if (!u->name && sv.state == ss_game)
  494.         ge->ClientCommand (sv_player);
  495.  
  496. //    SV_EndRedirect ();
  497. }
  498.  
  499. /*
  500. ===========================================================================
  501.  
  502. USER CMD EXECUTION
  503.  
  504. ===========================================================================
  505. */
  506.  
  507.  
  508.  
  509. void SV_ClientThink (client_t *cl, usercmd_t *cmd)
  510.  
  511. {
  512.     cl->commandMsec -= cmd->msec;
  513.  
  514.     if (cl->commandMsec < 0 && sv_enforcetime->value )
  515.     {
  516.         Com_DPrintf ("commandMsec underflow from %s\n", cl->name);
  517.         return;
  518.     }
  519.  
  520.     ge->ClientThink (cl->edict, cmd);
  521. }
  522.  
  523.  
  524.  
  525. #define    MAX_STRINGCMDS    8
  526. /*
  527. ===================
  528. SV_ExecuteClientMessage
  529.  
  530. The current net_message is parsed for the given client
  531. ===================
  532. */
  533. void SV_ExecuteClientMessage (client_t *cl)
  534. {
  535.     int        c;
  536.     char    *s;
  537.  
  538.     usercmd_t    nullcmd;
  539.     usercmd_t    oldest, oldcmd, newcmd;
  540.     int        net_drop;
  541.     int        stringCmdCount;
  542.     int        checksum, calculatedChecksum;
  543.     int        checksumIndex;
  544.     qboolean    move_issued;
  545.     int        lastframe;
  546.  
  547.     sv_client = cl;
  548.     sv_player = sv_client->edict;
  549.  
  550.     // only allow one move command
  551.     move_issued = false;
  552.     stringCmdCount = 0;
  553.  
  554.     while (1)
  555.     {
  556.         if (net_message.readcount > net_message.cursize)
  557.         {
  558.             Com_Printf ("SV_ReadClientMessage: badread\n");
  559.             SV_DropClient (cl);
  560.             return;
  561.         }    
  562.  
  563.         c = MSG_ReadByte (&net_message);
  564.         if (c == -1)
  565.             break;
  566.                 
  567.         switch (c)
  568.         {
  569.         default:
  570.             Com_Printf ("SV_ReadClientMessage: unknown command char\n");
  571.             SV_DropClient (cl);
  572.             return;
  573.                         
  574.         case clc_nop:
  575.             break;
  576.  
  577.         case clc_userinfo:
  578.             strncpy (cl->userinfo, MSG_ReadString (&net_message), sizeof(cl->userinfo)-1);
  579.             SV_UserinfoChanged (cl);
  580.             break;
  581.  
  582.         case clc_move:
  583.             if (move_issued)
  584.                 return;        // someone is trying to cheat...
  585.  
  586.             move_issued = true;
  587.             checksumIndex = net_message.readcount;
  588.             checksum = MSG_ReadByte (&net_message);
  589.             lastframe = MSG_ReadLong (&net_message);
  590.             if (lastframe != cl->lastframe) {
  591.                 cl->lastframe = lastframe;
  592.                 if (cl->lastframe > 0) {
  593.                     cl->frame_latency[cl->lastframe&(LATENCY_COUNTS-1)] = 
  594.                         svs.realtime - cl->frames[cl->lastframe & UPDATE_MASK].senttime;
  595.                 }
  596.             }
  597.  
  598.             memset (&nullcmd, 0, sizeof(nullcmd));
  599.             MSG_ReadDeltaUsercmd (&net_message, &nullcmd, &oldest);
  600.             MSG_ReadDeltaUsercmd (&net_message, &oldest, &oldcmd);
  601.             MSG_ReadDeltaUsercmd (&net_message, &oldcmd, &newcmd);
  602.  
  603.             if ( cl->state != cs_spawned )
  604.             {
  605.                 cl->lastframe = -1;
  606.                 break;
  607.             }
  608.  
  609.             // if the checksum fails, ignore the rest of the packet
  610.             calculatedChecksum = COM_BlockSequenceCRCByte (
  611.                 net_message.data + checksumIndex + 1,
  612.                 net_message.readcount - checksumIndex - 1,
  613.                 cl->netchan.incoming_sequence);
  614.  
  615.             if (calculatedChecksum != checksum)
  616.             {
  617.                 Com_DPrintf ("Failed command checksum for %s (%d != %d)/%d\n", 
  618.                     cl->name, calculatedChecksum, checksum, 
  619.                     cl->netchan.incoming_sequence);
  620.                 return;
  621.             }
  622.  
  623.             if (!sv_paused->value)
  624.             {
  625.                 net_drop = cl->netchan.dropped;
  626.                 if (net_drop < 20)
  627.                 {
  628.  
  629. //if (net_drop > 2)
  630.  
  631. //    Com_Printf ("drop %i\n", net_drop);
  632.                     while (net_drop > 2)
  633.                     {
  634.                         SV_ClientThink (cl, &cl->lastcmd);
  635.  
  636.                         net_drop--;
  637.                     }
  638.                     if (net_drop > 1)
  639.                         SV_ClientThink (cl, &oldest);
  640.  
  641.                     if (net_drop > 0)
  642.                         SV_ClientThink (cl, &oldcmd);
  643.  
  644.                 }
  645.                 SV_ClientThink (cl, &newcmd);
  646.             }
  647.  
  648.             cl->lastcmd = newcmd;
  649.             break;
  650.  
  651.         case clc_stringcmd:    
  652.             s = MSG_ReadString (&net_message);
  653.  
  654.             // malicious users may try using too many string commands
  655.             if (++stringCmdCount < MAX_STRINGCMDS)
  656.                 SV_ExecuteUserCommand (s);
  657.  
  658.             if (cl->state == cs_zombie)
  659.                 return;    // disconnect command
  660.             break;
  661.         }
  662.     }
  663. }
  664.  
  665.